-- ----------------------------------------------------------------------------
--        File: lSerial.lua
--      Author: Aitor Pérez Iturri
--     License: GNU General Public License
--        Date: 2011-02-08
-- Modificated: 2011-02-08
-- Description:
--
-- ----------------------------------------------------------------------------
--        TODO:
--
-- ----------------------------------------------------------------------------
--   Changelog:
--
-- ----------------------------------------------------------------------------

-- Module Definition
module("lSerial", package.seeall)
-- Imports {{{
require "luarocks.require"
require "alien"
require "bit"
require "struct"
require "lSerialConstants"
-- }}}
-- Global Variables {{{
Termios = {}
-- }}}
-- Local Variables {{{
local libc = alien.default
local bor = bit.bor
local band = bit.band
local buffer = alien.buffer(255)
-- glibc function interfaces {{{
libc.perror:types{
    ret = "void",
    "string"
}
libc.strerror:types{
    ret = "string",
    "int"
}
libc.open:types{
    ret="int",
    "string", "int"
}
libc.close:types{
    ret = "int",
    "int"
 } 
libc.tcgetattr:types{
    ret="int",
    "int", "pointer"
}
libc.tcsetattr:types{
    ret = "int",
    "int","int", "pointer"
}
libc.tcdrain:types{
    ret="int",
    "int"
}
libc.tcflow:types{
    ret="int",
    "int","int"
}
libc.tcflush:types{
    ret="int",
    "int","int"
}
libc.tcsendbreak:types{
    ret="int",
    "int","int"
}
libc.cfgetispeed:types{
    ret="int",
    "pointer"
}
libc.cfgetospeed:types{
    ret="int",
    "pointer"
}
libc.cfsetispeed:types{
    ret="int",
    "pointer","int"
}
libc.cfsetospeed:types{
    ret="int",
    "pointer","int"
}
libc.cfmakeraw:types{
    ret="void",
    "pointer"
}
libc.read:types{
	ret="int",
	"int", "pointer", "int"
}
libc.write:types{
	ret = "int",
	"int", "pointer", "int"
}-- }}}
-- glibc struct interfaces {{{
local termios = struct.defstruct{
    {"c_iflag", "int"}, -- 1
    {"c_oflag", "int"}, -- 5
    {"c_cflag", "int"}, -- 9
    {"c_lflag", "int"}, --
    {"c_line",  "char"},
    {"c_cc",    "char[32]"},
    {"c_ispeed","int"},
    {"c_ospeed","int"},
}
-- }}}
-- }}}
-- Local Functions {{{
local function get_mode (mode) -- {{{
    local mode = {}
    for f in mode:gmatch(".") do
    end
end --}}}
local close = function (serialhandler) -- {{{
    if libc.close(serialhandler.fd) >= 0 then
        return true
    end
    return false, libc.strerror(alien.errno())
end --}}}
local _tcgetattr = function(serialhandler, termios) -- {{{
    if (libc.tcgetattr(serialhandler.fd, termios) < 0) then
        return libc.strerror(alien.errno()) -- fail, return error
    end
    return nil -- Success, return nothing
end -- }}}
local _tcsetattr = function (serialhandler, termios, opts) --{{{
    local o = opts or TCSANOW
    if (libc.tcsetattr(serialhandler.fd, o, termios) < 0) then
        return libc.strerror(alien.errno())
    end
    return nil
end --}}}
local _tcdrain = function (serialhandler) --{{{
    if (libc.tcdrain(serialhandler.fd) < 0) then
        return false, libc.strerror(alien.errno())
    end
    return true
end --}}}
local _tcflush = function (serialhandler, opts) --{{{
    local o = opts or TCIOFLUSH
    if (libc.tcflush(serialhandler.fd, o) < 0) then
        return false, libc.strerror(alien.errno())
    end
    return true
end --}}}
local _tcsendbreak = function (serialhandler, time) --{{{
    local t = time or 0
    if (libc.tcsendbreak(serialhandler.fd, t) < 0) then
        return false, libc.strerror(alien.errno())
    end
    return true
end --}}}
local _tcflow = function (serialhandler, mode) --{{{
    if not mode then
        return true
    end
    if (libc.tcflow(serialhandler.fd, mode) < 0) then
        return false, libc.strerror(alien.errno())
    end
    return true
end --}}}
local _write = function (serialhandler, data) --{{{
	local n = libc.write(serialhandler.fd, data, data:len())
	if n < 0 then
		return n, libc.strerror(alien.errno())
	end
	return n
end --}}}
local _read = function (serialhandler, count)
	local c = count or 255
	local n = libc.read(serialhandler.fd, buffer, c)
	if n < 0 then
		return n, libc.strerror(alien.errno())
	end
	return n, buffer:tostring()
end
local _cfgetispeed = function (tio) --{{{
    return libc.cfgetispeed(tio())
end --}}}
local _cfgetospeed = function (tio) --{{{
    return libc.cfgetospeed(tio())
end --}}}
local _cfsetispeed = function (tio, speed) --{{{
    if (libc.cfsetispeed(tio(), speed) < 0) then
        return libc.strerror(alien.errno())
    end
    return nil
end --}}}
local _cfsetospeed = function (tio, speed) --{{{
    if (libc.cfsetospeed(tio(), speed) < 0) then
        return libc.strerror(alien.errno())
    end
    return nil
end --}}}
local _cfmakeraw = function (tio) --{{{
    return libc.cfmakeraw(tio())
end --}}}
local to_string = function (serialhandler) --{{{
    return string.format("serialHandler[%d]", serialhandler.fd)
end --}}}
-- serialHandler methods --{{{
local methods = {
    close = close,
    getAttributes = _tcgetattr,
    setAttributes = _tcsetattr,
	write = _write,
	read = _read,
	tcFlush = _tcflush,
	tcDrain = _tcdrain,
} --}}}
-- Termios methods -- {{{
local termios_methods = {
	makeRaw = _cfmakeraw,
	setInputSpeed = _cfsetispeed,
	setOutputSpeed = _cfsetospeed,
	getInputSpeed = _cfgetispeed,
	getOutputSpeed = _cfgetospeed
} -- }}}
-- }}}
-- Exported Functions {{{
open = function (device, mode)
    local t = {}
    t.fd = libc.open(device, mode)
    t.termios = termios:new()
    if (t.fd == -1) then
        return nil, libc.strerror(alien.errno())
        --return nil, "Error openning " .. device
    end
    return setmetatable(t, {
        __index=methods,
        __tostring = to_string,
    })
end

Termios.new = function ()
	local t = {}
	t.termios = termios:new()
	for n, m in pairs(termios_methods) do
		t[n] = m
	end
	return setmetatable(t, {
		__index = t.termios,
		__call = function () return t.termios() end,
		__tostring = function () 
			return string.format("termios struct [%s]", tostring(t.termios():topointer()))
		end
	})
end
--[[Termios.makeRaw = _cfmakeraw
Termios.setInputSpeed = _cfsetispeed
Termios.setOutputSpeed = _cfsetospeed
Termios.getInputSpeed = _cfgetispeed
Termios.getOutputSpeed = _cfgetospeed--]]
-- }}}
-- ---------------------------------------------------------------------------
-- vim:foldmethod=marker:tabstop=4
-- ---------------------------------------------------------------------------
